using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.ComponentModel;

namespace Tags.ID3
{
    /// <summary>
    /// Provide a class to read and write ID3v1
    /// </summary>
    public class ID3v1
    {
        private string _FilePath;
        private string _Title;
        private string _Artist;
        private string _Album;
        private string _Year;
        private string _Comment;
        private byte _TrackNumber;
        private byte _Genre;
        private bool _HaveTag;

        private bool _IsTemplate;
        /// <summary>
        /// Indicate if current ID3v1 is template
        /// </summary>
        [DefaultValue(false)]
        public bool IsTemplate
        {
            get { return _IsTemplate; }
            set { _IsTemplate = value; }
        }

        #region -> Public Properties <-

        /// <summary>
        /// Get file path of current ID3v1
        /// </summary>
        public string FilePath
        {
            get
            { return _FilePath; }
            internal set
            { _FilePath = value; }
        }

        /// <summary>
        /// Get file name of current ID3v1
        /// </summary>
        public string FileName
        {
            get
            { return Path.GetFileName(_FilePath); }
        }

        /// <summary>
        /// Get or set Title of current ID3v1
        /// </summary>
        public string Title
        {
            get
            { return _Title; }
            set
            {
                if (value.Length > 30)
                    throw (new ArgumentException("Title Length must be less than 30 characters"));

                _Title = value;
            }
        }

        /// <summary>
        /// Get or set Artist of current ID3v1
        /// </summary>
        public string Artist
        {
            get
            { return _Artist; }
            set
            {
                if (value.Length > 30)
                    throw (new ArgumentException("Artist Length must be less than 30 characters"));
                _Artist = value;
            }
        }

        /// <summary>
        /// Get or set Album of current ID3v1
        /// </summary>
        public string Album
        {
            get
            { return _Album; }
            set
            {
                if (value.Length > 30)
                    throw (new ArgumentException("Album Length must be less than 30 characters"));

                _Album = value;
            }
        }

        /// <summary>
        /// Get or set Year of current ID3v1
        /// </summary>
        public string Year
        {
            get
            { return _Year; }
            set
            {
                if (value.Length > 4)
                    throw (new ArgumentException("Year Length must be less than 4 characters"));
                _Year = value;
            }
        }

        /// <summary>
        /// Get or set Comment of current ID3v1
        /// </summary>
        public string Comment
        {
            get
            { return _Comment; }
            set
            {
                if (value.Length > 28)
                    throw (new ArgumentException("Comment Length must be less than 4 characters"));
                _Comment = value;
            }
        }

        /// <summary>
        /// Get or set TrackNumber of current ID3v1
        /// </summary>
        public byte TrackNumber
        {
            get
            { return _TrackNumber; }
            set
            { _TrackNumber = value; }
        }

        /// <summary>
        /// Get or set Genre of current ID3v1
        /// </summary>
        public byte Genre
        {
            get
            { return _Genre; }
            set
            { _Genre = value; }
        }

        /// <summary>
        /// Indicate if current File contain ID3v1 Information
        /// </summary>
        public bool HaveTag
        {
            get
            { return _HaveTag; }
            set
            { _HaveTag = value; }
        }

        #endregion

        /// <summary>
        /// Create new ID3v1 class
        /// </summary>
        /// <param name="FilePath">Path of file</param>
        /// <param name="LoadData">Indicate load data in constructor or not</param>
        public ID3v1(string FilePath, bool LoadData)
        {
            _FilePath = FilePath;
            _Title = "";
            _Artist = "";
            _Album = "";
            _Year = "";
            _Comment = "";
            _TrackNumber = 0;
            _Genre = 255;
            _HaveTag = false;

            if (LoadData)
                Load();
        }

        /// <summary>
        /// Load ID3v1 information from file
        /// </summary>
        public void Load()
        {
            TagStream FS = new TagStream(_FilePath, FileMode.Open);
            if (!FS.HaveID3v1()) // HaveID3v1 go to beginning of ID3v1 if exist
            {
                FS.Close();
                _HaveTag = false;
                return;
            }
            _Title = FS.ReadText(30, TextEncodings.Ascii);
            FS.Seek(FS.Length-95, SeekOrigin.Begin);
            _Artist = FS.ReadText(30, TextEncodings.Ascii);
            FS.Seek(FS.Length-65, SeekOrigin.Begin);
            _Album = FS.ReadText(30, TextEncodings.Ascii);
            FS.Seek(FS.Length-35, SeekOrigin.Begin);
            _Year = FS.ReadText(4, TextEncodings.Ascii);
            FS.Seek(FS.Length-31, SeekOrigin.Begin);
            _Comment = FS.ReadText(28, TextEncodings.Ascii);
            FS.Seek(FS.Length-2, SeekOrigin.Begin);
            _TrackNumber = FS.ReadByte();
            _Genre = FS.ReadByte();
            FS.Close();
            _HaveTag = true;
        }

        /// <summary>
        /// Save ID3v1 information to file
        /// </summary>
        public void Save()
        {
            TagStream fs = new TagStream(_FilePath, FileMode.Open);
            bool HTag = fs.HaveID3v1();
            if (HTag && !_HaveTag) // just delete ID3
                (fs).SetLength(fs.Length - 128);
            else if (!HTag && _HaveTag)
            {
                fs.Seek(fs.Length, SeekOrigin.Begin);
                fs.Write(GetTagBytes, 0, 128);
            }
            else if (HTag && _HaveTag)
            {
                fs.Seek(fs.Length-128, SeekOrigin.Begin);
                fs.Write(GetTagBytes, 0, 128);
            }
            fs.Close();
        }

        /// <summary>
        /// Clear all values
        /// </summary>
        public void ClearAll()
        {
            this.Album = "";
            this.Artist = "";
            this.Comment = "";
            this.Genre = 255;
            this.Title = "";
            this.TrackNumber = 0;
            this.Year = "";
        }

        /// <summary>
        /// Convert data tot Byte Array to write to file
        /// </summary>
        private byte[] GetTagBytes
        {
            get
            {
                byte[] Buf = new byte[128];
                Array.Clear(Buf, 0, 128);
                Encoding.Default.GetBytes("TAG").CopyTo(Buf, 0);
                Encoding.Default.GetBytes(_Title).CopyTo(Buf, 3);
                Encoding.Default.GetBytes(_Artist).CopyTo(Buf, 33);
                Encoding.Default.GetBytes(_Album).CopyTo(Buf, 63);
                Encoding.Default.GetBytes(_Year).CopyTo(Buf, 93);
                Encoding.Default.GetBytes(_Comment).CopyTo(Buf, 97);
                Buf[126] = _TrackNumber;
                Buf[127] = _Genre;
                return Buf;
            }
        }
    }
}
